/**
 * @file
 * @brief source of (module)
 * @details
 *  
 * @author buaa.byl
 * @date 2012-02-14 created
 */

/* ************************************************************************* */
/*              include files                                                */
/* ************************************************************************* */
#define _CRTDBG_MAP_ALLOC

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

#ifdef _MSC_VER
#include <windows.h>
#include <crtdbg.h>
#endif

#include "getopt_win32.h"

//#define DEBUG_GETOPT_AVAILABLE

/* ************************************************************************* */
/*              declare of file-based type or struct                         */
/* ************************************************************************* */
#define OPT_OPTS    1
#define OPT_FLAGS   2
#define OPT_ORPHAN  3


/* ************************************************************************* */
/*              define of file-based variable                                */
/* ************************************************************************* */


/* ************************************************************************* */
/*              declare of file-based interface                              */
/* ************************************************************************* */
static char* strdup2(const char* s);


/* ************************************************************************* */
/*              define of module interface                                   */
/* ************************************************************************* */
GETOPT_INFO* getopt_win32(int argc, const char* argv[], const char* optstring)
{
    int i = 0;
    const char* p = optstring;
    char prev = 0;
    char tmp  = 0;

    char format_map[256] = {0};
    int  opts_num = 0;
    int  args_num = 0;

    GETOPT_INFO* pinfo_ret = NULL;
    GETOPT_INFO* pinfo_tmp = NULL;

    //Pre check
    if (argc == 1) {
        printf("getopt.GetoptError: no argument\n");
        return NULL;
    }

    //Prepare
    while (*p) {
        if (isalnum(*p) || (*p == ':')) {
            if (*p == ':') {
                format_map[prev & 0xFFu] = OPT_OPTS;
            } else {
                format_map[*p & 0xFFu] = OPT_FLAGS;
                prev = *p;
            }
        }
        p++;
    }

#ifdef DEBUG_GETOPT_AVAILABLE
    printf("available:\n");
    for (i = 0;i < 256;i++) {
        if (format_map[i] == OPT_OPTS) {
            printf(" -%c xxx\n", i);
        } else if (format_map[i] == OPT_FLAGS){
            printf(" -%c\n", i);
        }
    }
#endif

    //Check
    i = 1;
    opts_num = 0;
    args_num = 0;
    while (i < argc) {
        if ((strlen(argv[i]) == 2) && (argv[i][0] == '-')) {
            tmp = argv[i][1];
            if (format_map[tmp] == OPT_FLAGS) {
                opts_num++;
            } else if (format_map[tmp] == OPT_OPTS) {
                i++;
                if (i < argc) {
                    opts_num++;
                } else {
                    printf("getopt.GetoptError: option -%c requires argument\n", tmp);
                    return NULL;
                }
            } else {
                printf("getopt.GetoptError: option -%c not recognized\n", tmp);
                return NULL;
            }// if (format_map[tmp] == OPTION) {
        } else {
            args_num++;
        }// if ((strlen(argv[i]) == 2) && (argv[i][0] == '-')) {

        i++;
    }

    //Parser
    pinfo_ret = (GETOPT_INFO*)malloc(sizeof(GETOPT_INFO));
    memset(pinfo_ret, 0, sizeof(GETOPT_INFO));

    pinfo_ret->nr_opts = 0;
    pinfo_ret->nr_args = 0;

    pinfo_ret->opts = (struct _GETOPT_OPTS*)malloc(opts_num * sizeof(struct _GETOPT_OPTS));
    pinfo_ret->args = (char**)malloc(args_num * sizeof(char*));

    i = 1;
    while (i < argc) {
        if ((strlen(argv[i]) == 2) && (argv[i][0] == '-')) {
            tmp = argv[i][1];
            if (format_map[tmp] == OPT_FLAGS) {
                pinfo_ret->opts[pinfo_ret->nr_opts].opt = tmp;
                pinfo_ret->opts[pinfo_ret->nr_opts].arg = NULL;
            } else if (format_map[tmp] == OPT_OPTS) {
                i++;
                pinfo_ret->opts[pinfo_ret->nr_opts].opt = tmp;
                pinfo_ret->opts[pinfo_ret->nr_opts].arg = strdup2(argv[i]);
            }// if (format_map[tmp] == OPTION) {
            pinfo_ret->nr_opts++;
        } else {
            pinfo_ret->args[pinfo_ret->nr_args] = strdup2(argv[i]);
            pinfo_ret->nr_args++;
        }// if ((strlen(argv[i]) == 2) && (argv[i][0] == '-')) {

        i++;
    }

    return pinfo_ret;
}

void getopt_free(GETOPT_INFO* p)
{
    int i = 0;
    if (p != NULL) {
        if (p->opts != NULL) {
            for (i = 0;i < p->nr_opts;i++) {
                if (p->opts[i].arg != NULL) {
                    free((void*)p->opts[i].arg);
                }
            }
            free(p->opts);
        }
        if (p->args != NULL) {
            for (i = 0;i < p->nr_args;i++) {
                if (p->args[i] != NULL) {
                    free((void*)p->args[i]);
                }
            }
            free((void*)p->args);
        }
        free(p);
    }
}


/* ************************************************************************* */
/*              define of file-based interface                               */
/* ************************************************************************* */
char* strdup2(const char* s)
{
    const char* p = s;
    char* ret = NULL;

    if (s == NULL) {
        return NULL;
    }

    while (*p++) {
    }

    ret = (char*)malloc((p - s + 1) * sizeof(char));
    memcpy(ret, s, (p - s + 1));

    return ret;
}

